package com.latex_demo;

import android.content.Context;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.view.Gravity;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.IViewManagerWithChildren;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.text.ReactTextShadowNode;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.text.TextAttributeProps;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.text.TextLayoutManager;
import com.facebook.yoga.YogaMeasureMode;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.latex_demo.Tokenizer.*;
import com.orhanobut.logger.Logger;

import static com.latex_demo.Tokenizer.tokenizer;


@ReactModule(name = MathViewManager.REACT_CLASS)
public class MathViewManager extends MathViewAnchorViewManager<RCTMathView, ReactTextShadowNode>
        implements IViewManagerWithChildren {
    @VisibleForTesting
    public static final String REACT_CLASS = "RCTMathView";

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    public RCTMathView createViewInstance(ThemedReactContext context) {
        return new RCTMathView(context);
    }

    @Override
    public void updateExtraData(RCTMathView view, Object extraData) {
        ReactTextUpdate update = (ReactTextUpdate) extraData;
        if (update.containsImages()) {
            Spannable spannable = update.getText();
            TextInlineImageSpan.possiblyUpdateInlineImageSpans(spannable, view);
        }
        view.setText(update);
    }

    @Override
    public ReactTextShadowNode createShadowNodeInstance() {
        return new ReactTextShadowNode();
    }

    @Override
    public Class<ReactTextShadowNode> getShadowNodeClass() {
        return ReactTextShadowNode.class;
    }

    @Override
    protected void onAfterUpdateTransaction(RCTMathView view) {
        super.onAfterUpdateTransaction(view);
        view.updateView();
    }

    public boolean needsCustomLayoutForChildren() {
        return true;
    }

    @Override
    public Object updateState(
            RCTMathView view, ReactStylesDiffMap props, @Nullable StateWrapper stateWrapper) {
        // TODO T55794595: Add support for updating state with null stateWrapper
        ReadableNativeMap state = stateWrapper.getState();
        ReadableMap attributedString = state.getMap("attributedString");
        ReadableMap paragraphAttributes = state.getMap("paragraphAttributes");

        Spannable spanned =
                TextLayoutManager.getOrCreateSpannableForText(view.getContext(), attributedString);
        view.setSpanned(spanned);

        TextAttributeProps textViewProps = new TextAttributeProps(props);

        int textBreakStrategy =
                getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy"));

        // TODO add justificationMode prop into local Data
        int justificationMode = Layout.JUSTIFICATION_MODE_NONE;

        return new ReactTextUpdate(
                spanned,
                state.hasKey("mostRecentEventCount") ? state.getInt("mostRecentEventCount") : -1,
                false, // TODO add this into local Data
                textViewProps.getTextAlign(),
                textBreakStrategy,
                justificationMode);
    }

    private int getTextBreakStrategy(@Nullable String textBreakStrategy) {
        int androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
        if (textBreakStrategy != null) {
            switch (textBreakStrategy) {
                case "simple":
                    androidTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
                    break;
                case "balanced":
                    androidTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED;
                    break;
                default:
                    androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
                    break;
            }
        }
        return androidTextBreakStrategy;
    }

    @Override
    public @Nullable
    Map getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.of(
                "topTextLayout", MapBuilder.of("registrationName", "onTextLayout"),
                "topInlineViewLayout", MapBuilder.of("registrationName", "onInlineViewLayout"));
    }

    @Override
    public long measure(
            Context context,
            ReadableMap localData,
            ReadableMap props,
            ReadableMap state,
            float width,
            YogaMeasureMode widthMode,
            float height,
            YogaMeasureMode heightMode) {

        return TextLayoutManager.measureText(
                context, localData, props, width, widthMode, height, heightMode);
    }

    @Override
    public void setPadding(RCTMathView view, int left, int top, int right, int bottom) {
        view.setPadding(left, top, right, bottom);
    }

    @ReactProp(name="math")
    public void setMath(RCTMathView view,String mathText){
//        mathText = mathText.replaceAll("\u00AD", "");
//        List<Attachment> attachmentList = new ArrayList<>();
//        List<Tokenizer.TOKEN> tokenList = tokenizer(mathText, attachmentList);
//        for (Tokenizer.TOKEN token : tokenList) {
//            if (token instanceof Tokenizer.ATTACHMENT) {
//                attachmentList.remove(((Tokenizer.ATTACHMENT) token).attachment);
//            }
//        }
//        List<Object> result = until(END.class);

        Logger.e(mathText);
        ReactTextUpdate textUpdate = new ReactTextUpdate(
                new SpannableString(mathText),
               -1,
                false, // TODO add this into local Data
                Gravity.LEFT,
                Layout.BREAK_STRATEGY_HIGH_QUALITY,
                Layout.JUSTIFICATION_MODE_NONE
        );
        view.setText(textUpdate);

    }
}
